package com.guo.rpc.config;

import com.guo.rpc.annotation.Reference;
import com.guo.rpc.bean.ClassIdBean;
import com.guo.rpc.spring.RefrenceBean;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: ljguo4
 * @email: ljguo4@iflytek.com
 * @Date: 2021/7/22 11:21
 * @Description:
 * @version: 1.0
 */
@Configuration
public class RpcConsumerAutoConfigration {

    private static final Map<ClassIdBean, Object> REMOTE_REFRENCE_MAP = new ConcurrentHashMap<>();

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public BeanPostProcessor beanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

                Class<?> objClz;
                if (AopUtils.isAopProxy(bean)) {
                    objClz = AopUtils.getTargetClass(bean);
                } else {
                    objClz = bean.getClass();
                }

                // 1. 遍历class找到属性含有refrence标签得字段, 并提取有用信息
                // 2. 创建refrenceBean 准备创建 remoteRefrenceBean 远程调用bean
                // 3. 赋值给field
                try {
                    for (Field field : objClz.getDeclaredFields()) {
                        Reference refrence = field.getAnnotation(Reference.class);

                        // 获取接口版本号
                        String version = refrence.version();

                        // 获取refrenceBean
                        RefrenceBean refrenceBean = RpcConsumerAutoConfigration.this.getConsumerBean(field, version);

                        // 组装key
                        ClassIdBean classIdBean = new ClassIdBean(refrenceBean.getInterfaceClass(), version);

                        Object remoteRefrenceBean =
                                RpcConsumerAutoConfigration.REMOTE_REFRENCE_MAP.get(classIdBean);
                        if (remoteRefrenceBean == null) {
                            synchronized (this) {
                                // dcl
                                remoteRefrenceBean = RpcConsumerAutoConfigration.REMOTE_REFRENCE_MAP.get(classIdBean);
                                if (remoteRefrenceBean == null) {
                                    Object object = refrenceBean.getObject();
                                    RpcConsumerAutoConfigration.REMOTE_REFRENCE_MAP.put(classIdBean, object);
                                }
                            }
                        }
                        field.setAccessible(true);
                        field.set(bean, remoteRefrenceBean);
                    }
                } catch (Exception e) {
                    throw new BeanCreationException(beanName, e);
                }

                return bean;
            }

            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                return bean;
            }
        };
    }

    private RefrenceBean getConsumerBean(Field field, String version) {
        RefrenceBean refrenceBean = new RefrenceBean();
        refrenceBean.setInterfaceClass(field.getType());
        refrenceBean.setVersion(version);
        return refrenceBean;
    }

}
